package com.itheima.springbootsecurity.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.springbootsecurity.domain.LoginUser;
import com.itheima.springbootsecurity.domain.User;
import com.itheima.springbootsecurity.exception.CustomAuthenticationException;
import com.itheima.springbootsecurity.mapper.UserMapper;
import com.itheima.springbootsecurity.service.UserService;
import com.itheima.springbootsecurity.utils.JWTUtils;
import com.itheima.springbootsecurity.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author faithwu
 * @since 2024-07-12
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JWTUtils jwtUtils;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public User findByUsername(String username) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        return baseMapper.selectOne(queryWrapper);
    }

    @Override
    public String login(User user) {
        // 把登录时候的用户名与密码封装成一个authentication认证信息对象
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        // 通过AuthenticationManager的authenticate方法进行用户认证
        Authentication authenticate = authenticationManager.authenticate(authentication);
        if (Objects.isNull(authenticate)) {
            // 如果authenticate对象为空则表示校验失败
            throw new CustomAuthenticationException("登录失败");
        }

//        // 校验通过，将用户信息放入LoginUser中
//        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
//        // 生成jwt
//        Map<String, Object> claims = new HashMap<>();
//        claims.put("claims", JSON.toJSONString(loginUser));

        // 校验通过，查询用户信息
        User currentUser = findByUsername(user.getUsername());
        // 生成jwt
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", currentUser.getId());
        claims.put("username", currentUser.getUsername());

        String token = jwtUtils.createToken(claims);

        // 将token存储到redis中
        try {
            stringRedisTemplate.opsForValue().set(jwtUtils.getTokenRedisKeyPrefix() + currentUser.getUsername(), token, jwtUtils.getExpiration(), TimeUnit.SECONDS);
        }catch (Exception ex) {
            ex.printStackTrace();
            throw new CustomAuthenticationException("登录失败，存储token失败");
        }

        return token;
    }

    @Override
    public boolean logout(HttpServletRequest request, HttpServletResponse response) {
        LoginUser loginUser = ThreadLocalUtil.get();

        // 从SecurityContextHolder中获取当前用户的认证信息Authentication
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            // 清空用户认证信息上下文
            new SecurityContextLogoutHandler().logout(request, response, authentication);
            // 清空redis中的token
            stringRedisTemplate.delete(jwtUtils.getTokenRedisKeyPrefix() + loginUser.getUser().getUsername());
        }

        return true;
    }
}
